#include "backdrop_editor.h"
#include "backdrop.h"
#include "decal.h"
#include "transient_decal.h"
#include "font/font_manager.h"
#include "ui/ui_sprite.h"

#include "graphics/graphics_util.h"
#include <math.h>
#include <stdlib.h>

#include "util/mmgr.h"
#include "util/common_macros.h"

namespace backdrop
{

//
// DECAL_BRUSH /////////////////////////////////////////////////////////////////////////////////////////////////////////////
//

class DecalBrush : public BackdropBrush
{
public:
	DecalBrush(BackdropEditor* pEditor, const std::string& Name) : BackdropBrush(pEditor, Name) {
		m_Sprite.Create(Name);
	}

	virtual ~DecalBrush() {
	}

	virtual void Tick(float dt) {
		if (m_Sprite.IsValid()) m_Sprite.Tick(dt);
	}

	virtual void Render() {
		if (m_Sprite.IsValid()) m_Sprite.Render();
	}

	virtual void FollowMouse(const math::Vec2& Pos) {
		m_Pos = Pos - (0.5f * m_Sprite.m_Size);
		if (m_Sprite.IsValid()) m_Sprite.Setup(m_Pos - m_pEditor->GetOffset(), m_Sprite.m_Size);
	}

	virtual void Place() {
		std::string SpriteName = m_Name;
		if (!m_Type.empty()) SpriteName += "[" + m_Type + "]";
		if (Decal* pDecal = m_pEditor->GetBackdrop()->CreateDecalSprite(SpriteName, m_Pos))
			m_pEditor->GetBackdrop()->AddDecalSprite(pDecal);
	}

	virtual void Rotate(const int& Dir = 0) {
	}

protected:
	ui::UISprite	m_Sprite;
};

//
// TRANSIENT_DECAL_BRUSH ///////////////////////////////////////////////////////////////////////////////////////////////////
//

class TransientBrush : public BackdropBrush
{
public:
	TransientBrush(BackdropEditor* pEditor, const std::string& Name) : BackdropBrush(pEditor, Name), m_bPlaced(false), m_LockAxis(0) {
		m_Sprite.Create(Name);
	}

	virtual ~TransientBrush() {
	}

	virtual void Tick(float dt) {
		if (m_Sprite.IsValid()) m_Sprite.Tick(dt);
	}

	virtual void Render() {
		if (m_Sprite.IsValid()) m_Sprite.Render();
		if (m_bPlaced) {
			// draw line
			Graphics::Vertex2D VertA[12];
			math::Vec2 Offset = 0.5f * m_Sprite.m_Size;
			int Index = GraphicsUtil::FillLine(m_Origin+Offset, m_Pos+Offset, 0.5f/800.0f, 0xFFFFFF00, 0xFFFFFF00, VertA);
			GetGraphics()->SetTexture(0, NULL);
			GetGraphics()->DrawVertex2D(VertA, Index);
			// draw metric
			char szMetric[256];
			math::Vec2 Dir = m_Origin - m_Pos;
			sprintf(szMetric, "%f", Dir.Magnitude());
			FontManager::Instance().Print(0.5f * (m_Origin + m_Pos) + Offset, szMetric);
		}
	}

	virtual void FollowMouse(const math::Vec2& Pos) {
		m_Pos = Pos - (0.5f * m_Sprite.m_Size) - m_pEditor->GetOffset();
		if (m_bPlaced) {
			math::Vec2 dist = m_Origin - m_Pos;
			if (dist.Magnitude() < (1.0f / 800.0f))
				m_Pos = m_Origin;
			else if (input::Keyboard* pKB = &GET_KEYBOARD()) {
				if (pKB->IsShiftDown())
				{
					if (m_LockAxis == 0)
					{
						if (fabs(dist.x) > fabs(dist.y))
							m_LockAxis = 2;
						else
							m_LockAxis = 1;
					}
				}
				else
					m_LockAxis = 0;
			}

			switch (m_LockAxis)
			{
			case 1:
				m_Pos.x = m_Origin.x;
				break;
			case 2:
				m_Pos.y = m_Origin.y;
				break;
			}
		}
		if (m_Sprite.IsValid())
			m_Sprite.Setup(m_bPlaced ? m_Origin : m_Pos, m_Sprite.m_Size);
	}

	virtual void Place() {
		if (m_bPlaced)
		{
			std::string SpriteName = m_Name;
			int Layer = m_Type.empty() ? 0 : atoi(m_Type.c_str());
			m_pEditor->GetBackdrop()->AddTransientSprite(m_Name, m_Origin, m_Pos - m_Origin, Layer);
		}
		else
			m_Origin = m_Pos;

		m_bPlaced = !m_bPlaced;
	}

	virtual void Rotate(const int& Dir = 0) {
	}

protected:
	ui::UISprite	m_Sprite;
	math::Vec2		m_Origin;
	int				m_LockAxis;
	bool			m_bPlaced;
};

//
// BACKDROP_EDITOR /////////////////////////////////////////////////////////////////////////////////////////////////////////
//

BackdropEditor::BackdropEditor(Backdrop* pBackdrop) :
	m_pBackdrop(pBackdrop),
	m_pBrush(NULL),
	m_EditMode(MODE_DEFAULT),
	m_Instructions(""),
	m_AltInstructions(""),
	m_BrushType(""),
	m_InstructOffset(0.05f, 0.05f),
	m_StatusOffset(0.05f, 0.5f),
	m_CurStatus(0)
{
}

BackdropEditor::~BackdropEditor()
{
	Clear();
}

void BackdropEditor::Clear()
{
	UTIL_SAFE_DELETE(m_pBrush);
}

void BackdropEditor::Tick(float dt)
{
	if (m_pBrush) m_pBrush->Tick(dt);
}

void BackdropEditor::Render()
{
	if (m_pBrush) m_pBrush->Render();

	RenderInstructions();
	RenderStatus();
	RenderFileName();
	RenderGridLines();
}

void BackdropEditor::MouseButtonPressed(input::Mouse::BUTTON_STATE* pKey, const math::Vec2& Pos)
{
	if (m_pBrush) m_pBrush->FollowMouse(Pos + GetOffset());

	switch (pKey->ButtonID)
	{
	case input::Mouse::BUTTON_LEFT:

		if (m_pBrush)
		{
			// place brush
			AddStatusMsg("Place brush");
			m_pBrush->Place();
			m_pBrush->FollowMouse(Pos + GetOffset());
		}
		else
		{
			// copy brush
			if (CopyBrushSpecial(Pos + GetOffset(), false))
			{
				RefreshAltText();
			}
			if (Decal* pDecal = m_pBackdrop->GetDecalSprite(Pos + GetOffset()))
			{
				m_EditMode = MODE_DECAL;
				AddStatusMsg("Get brush");
				m_pBrush = new DecalBrush(this, pDecal->GetDecalName());
				m_pBrush->FollowMouse(Pos + GetOffset());

				RefreshAltText();
			}
			else if (TransientDecal* pTrans = m_pBackdrop->GetTransientSprite(Pos))
			{
				m_EditMode = MODE_TRANSIENT;
				AddStatusMsg("Get brush");
				m_pBrush = new TransientBrush(this, pTrans->GetName());
				m_pBrush->FollowMouse(Pos);

				RefreshAltText();
			}
			else if (CopyBrushSpecial(Pos + GetOffset(), true))
			{
				RefreshAltText();
			}
		}
		break;

	case input::Mouse::BUTTON_RIGHT:

		// RMB always deletes
		if (DeleteSpecial(Pos + GetOffset(), false)) break;

		if (Decal* pDecal = m_pBackdrop->GetDecalSprite(Pos + GetOffset()))
		{
			AddStatusMsg("Delete decal");
			m_pBackdrop->DeleteDecalSprite(pDecal);
		}
		else if (TransientDecal* pTrans = m_pBackdrop->GetTransientSprite(Pos))
		{
			AddStatusMsg("Delete transient");
			m_pBackdrop->DeleteTransientSprite(pTrans);
		}
		else
			DeleteSpecial(Pos + GetOffset(), true);

		break;
	}
}

void BackdropEditor::MouseWheelMove(int WheelInfo, const math::Vec2& Pos)
{
	if (m_pBrush) m_pBrush->Rotate(WheelInfo);
}

void BackdropEditor::MouseMove(const math::Vec2& From, const math::Vec2& To)
{
	if (m_pBrush) m_pBrush->FollowMouse(To + GetOffset());
	else if (input::Mouse::BUTTON_STATE* bs = GET_MOUSE().GetButtonState(input::Mouse::BUTTON_RIGHT))
	{
		if (bs->ButtonDown)
			MouseButtonPressed(bs, To + GetOffset());
	}
}

void BackdropEditor::KeyPressed(input::Keyboard::KEY* pKey)
{
	input::Keyboard* pKB = &GET_KEYBOARD();
	bool bCtrl = pKB->IsCtrlDown();

	switch (pKey->KeyID)
	{
	case KEY_B:
		AddStatusMsg("Edit background");
		BackgroundFunction();
		break;
	case KEY_D:
		AddStatusMsg("Edit decals");
		DecalsFunction();
		break;
	case KEY_X:
		if (bCtrl) {
			AddStatusMsg("Clear all");
			ClearAllFunction();
			break;
		}
		else if (m_pBrush) {
			AddStatusMsg("Clear brush");
			UTIL_SAFE_DELETE(m_pBrush);
		}
		break;
	case KEY_F7:
		AddStatusMsg("Load level");
		LoadFunction();
		break;
	case KEY_F8:
		AddStatusMsg("Save level");
		SaveFunction(bCtrl);
		break;
	case KEY_F9:
		AddStatusMsg("Exit Editor");
		ExitFunction();
		break;
	case KEY_PRIOR:
		if (bCtrl) {
			AddStatusMsg("Load prev level");
			LoadNextFunction(false);
		}
		break;
	case KEY_NEXT:
		if (bCtrl) {
			AddStatusMsg("Load next level");
			LoadNextFunction(true);
		}
		break;
	}
}

void BackdropEditor::SetDecalBrush(const std::string& Name, const std::string& Type)
{
	AddStatusMsg("Decal selected");

	UTIL_SAFE_DELETE(m_pBrush);
	m_pBrush = new DecalBrush(this, Name);
	m_pBrush->FollowMouse( GET_MOUSE().GetPos() + GetOffset() );

	if (Type.empty())
		m_pBrush->SetType(m_BrushType);
	else
		SetDecalBrushType(Type);
}

void BackdropEditor::SetTransientBrush(const std::string& Name, int Layer)
{
	AddStatusMsg("Transient selected");

	UTIL_SAFE_DELETE(m_pBrush);
	m_pBrush = new TransientBrush(this, Name);
	m_pBrush->FollowMouse( GET_MOUSE().GetPos() + GetOffset() );

	if (Layer < 0)
		m_pBrush->SetType(m_BrushType);
	else {
		char szLayer[256];
		sprintf(szLayer, "%d", Layer);
		m_pBrush->SetType(szLayer);
	}
}

void BackdropEditor::AddStatusMsg(const std::string& Message)
{
	m_StatusMsg[m_CurStatus] = Message;
	m_CurStatus = (m_CurStatus + 1) % STATUS_LINES;
}

void BackdropEditor::SetDecalBrushType(const std::string& Type)
{
	if (!Type.empty()) {
		if (m_EditMode == MODE_TRANSIENT)
			AddStatusMsg("Set transient to layer: " + Type);
		else
			AddStatusMsg("Add decal to type: [" + Type + "]");
	}

	m_BrushType = Type;
	if (m_pBrush) m_pBrush->SetType(Type);
}

void BackdropEditor::RenderInstructions()
{
	std::string Msg;

	if (m_Instructions.empty())
		Msg = "F7  - Load\nF8  - Save\nF9  - Exit\nB   - Background\nD   - Decorations\nX   - Clear Brush\n\nCtrl+PgUp - Load Prev\nCtrl+PgDn - Load Next";
	else
		Msg = m_Instructions;

	if (!m_AltInstructions.empty()) Msg += "\n" + m_AltInstructions;

	FontManager::Instance().Print(m_InstructOffset, Msg.c_str());
}

void BackdropEditor::RenderStatus()
{
	math::Vec2 VPos = m_StatusOffset;
	int Pos;

	for (int i = 0; i < (STATUS_LINES - 1); i++)
	{
		Pos = (m_CurStatus + i + 1) % STATUS_LINES;
		if (m_StatusMsg[Pos].empty()) continue;

		FontManager::Instance().Print(VPos, m_StatusMsg[Pos].c_str());
		VPos.y += FontManager::Instance().GetDefaultFont()->GetHeight();
	}

	FontManager::Instance().Print(VPos, ">");
}

void BackdropEditor::RenderFileName()
{
	std::string FName("CURRENT FILE: ");
	std::string file = m_pBackdrop->GetFileName();
	math::Vec2 Pos;

	if (file.empty())
		file = "none";

	FName += file;
	Pos.x = FontManager::Instance().GetDefaultFont()->GetHeight();
	Pos.y = 1.0f - (2.0f * Pos.x);

	FontManager::Instance().Print(Pos, FName.c_str());
}

void BackdropEditor::RenderGridLines()
{
	Graphics::Vertex2D VertA[24];
	int Index = 0;
	Graphics* pG = GetGraphics();

	Index = GraphicsUtil::FillLine(math::Vec2(0.5f, 0.0f), math::Vec2(0.5f, 1.0f), 0.5f/800.0f, 0x7FFFFFFF, 0x7FFFFFFF, VertA);
	Index += GraphicsUtil::FillLine(math::Vec2(0.0f, 0.5f), math::Vec2(1.0f, 0.5f), 0.5f/600.0f, 0x7FFFFFFF, 0x7FFFFFFF, &VertA[Index]);

	pG->SetTexture(0, NULL);
	pG->DrawVertex2D(VertA, Index);
}

}  // namespace backdrop